﻿using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Http;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using Microsoft.WindowsAzure.Storage.Shared.Protocol;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace Sino.CapacityCloud.Services
{

    public class AzureStorageService
        : ConfigurableServiceBase, IAzureStorageService
    {

        private readonly IHttpContextAccessor _httpContextAccessor;

        public AzureStorageService(IHostingEnvironment env, IHttpContextAccessor httpContextAccessor)
            : base(env, section: "Data:DefaultConnection")
        {
            this._httpContextAccessor = httpContextAccessor;
        }

        public static void ClearContainerPermittions(CloudBlobContainer container)
        {
            //Clear any existing access policies on container.
            BlobContainerPermissions perms = container.GetPermissions();
            perms.SharedAccessPolicies.Clear();
            container.SetPermissions(perms);
        }

        public static void CreateSharedAccessPolicy(CloudBlobClient blobClient, CloudBlobContainer container, string policyName, SharedAccessBlobPermissions permittion = SharedAccessBlobPermissions.Read | SharedAccessBlobPermissions.Write, DateTime? startTime = null, DateTime? expiryTime = null)
        {
            //Create a new shared access policy and define its constraints.
            SharedAccessBlobPolicy sharedPolicy = new SharedAccessBlobPolicy()
            {
                SharedAccessStartTime = startTime.HasValue ? startTime.Value : DateTime.UtcNow.AddMinutes(-5),
                SharedAccessExpiryTime = expiryTime.HasValue ? expiryTime.Value : DateTime.UtcNow.AddHours(24),
                Permissions = permittion
            };

            //Get the container's existing permissions.
            BlobContainerPermissions permissions = container.GetPermissions();

            //Add the new policy to the container's permissions, and set the container's permissions.
            permissions.SharedAccessPolicies.Add(policyName, sharedPolicy);
            container.SetPermissions(permissions);
        }

        public static string GetContainerSasUri(CloudBlobContainer container, SharedAccessBlobPermissions permittion = SharedAccessBlobPermissions.Read | SharedAccessBlobPermissions.Write, DateTime? startTime = null, DateTime? expiryTime = null)
        {
            //Set the expiry time and permissions for the container.
            //In this case no start time is specified, so the shared access signature becomes valid immediately.
            SharedAccessBlobPolicy sasConstraints = new SharedAccessBlobPolicy();
            sasConstraints.SharedAccessStartTime = startTime.HasValue ? startTime.Value : DateTime.UtcNow.AddMinutes(-5);
            sasConstraints.SharedAccessExpiryTime = expiryTime.HasValue ? expiryTime.Value : DateTime.UtcNow.AddHours(24);
            sasConstraints.Permissions = permittion;

            //Generate the shared access signature on the container, setting the constraints directly on the signature.
            string sasContainerToken = container.GetSharedAccessSignature(sasConstraints);

            //Return the URI string for the container, including the SAS token.
            return container.Uri + sasContainerToken;
        }

        public static async Task<string> UploadAndGetBlobSasUriAsync(CloudBlobContainer container, string fileName, Stream stream, bool overwritten = true, string policyName = null, SharedAccessBlobPermissions permittion = SharedAccessBlobPermissions.Read | SharedAccessBlobPermissions.Write, DateTime? startTime = null, DateTime? expiryTime = null)
        {
            //Get a reference to a blob within the container.
            CloudBlockBlob blob = container.GetBlockBlobReference(fileName);

            if (!(await blob.ExistsAsync() && !overwritten))
            {
                //Upload text to the blob. If the blob does not yet exist, it will be created.
                //If the blob does exist, its existing content will be overwritten.
                await blob.UploadFromStreamAsync(stream);
            }

            //Set the expiry time and permissions for the blob.
            //In this case the start time is specified as a few minutes in the past, to mitigate clock skew.
            //The shared access signature will be valid immediately.
            SharedAccessBlobPolicy sasConstraints = new SharedAccessBlobPolicy();
            sasConstraints.SharedAccessStartTime = startTime.HasValue ? startTime.Value : DateTime.UtcNow.AddMinutes(-5);
            sasConstraints.SharedAccessExpiryTime = expiryTime.HasValue ? expiryTime.Value : DateTime.UtcNow.AddHours(24);
            sasConstraints.Permissions = permittion;

            //Generate the shared access signature on the blob, setting the constraints directly on the signature.
            var sasBlobToken = "";
            if (policyName == null)
            {
                sasBlobToken = blob.GetSharedAccessSignature(sasConstraints);
            }
            else
            {
                sasBlobToken = blob.GetSharedAccessSignature(sasConstraints, policyName);
            }

            //Return the URI string for the container, including the SAS token.
            return blob.Uri + sasBlobToken;
        }

        public async Task<string> GenerateShareAccessSignitureAsync(string containerName, SharedAccessBlobPermissions permittion = SharedAccessBlobPermissions.Read | SharedAccessBlobPermissions.Write, DateTime? startTime = null, DateTime? expiryTime = null)
        {
            //Parse the connection string and return a reference to the storage account.
            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(this.Configuration["StorageConnectionString"]);

            //Create the blob client object.
            CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

            var context = this._httpContextAccessor.HttpContext;

            //ServiceProperties propers = blobClient.GetServiceProperties();
            //CorsRule cr = new CorsRule();
            //cr.AllowedMethods = CorsHttpMethods.Put | CorsHttpMethods.Options | CorsHttpMethods.Post | CorsHttpMethods.Delete | CorsHttpMethods.Get;
            //cr.AllowedOrigins.Add("*");
            ////cr.AllowedOrigins.Add("http://ccp.vip56.cn");
            ////cr.AllowedOrigins.Add("http://ccp-dev.chinacloudapp.cn:5000");
            ////if (context?.Request != null)
            ////{
            ////    cr.AllowedOrigins.Add($"{context.Request.Scheme}://{context.Request.Host}");
            ////}
            //cr.AllowedHeaders.Add("*");
            //cr.ExposedHeaders.Add("*");
            //cr.MaxAgeInSeconds = 200;
            //propers.Cors = new CorsProperties();
            //propers.Cors.CorsRules.Add(cr);
            //blobClient.SetServiceProperties(propers);

            //Get a reference to a container to use for the sample code, and create it if it does not exist.
            CloudBlobContainer container = blobClient.GetContainerReference(containerName);
            container.CreateIfNotExists();

            return await Task.FromResult(GetContainerSasUri(container, permittion, startTime, expiryTime));
        }

        public async Task<bool> RemoveContainerAsync(string containerName)
        {
            //Parse the connection string and return a reference to the storage account.
            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(this.Configuration["StorageConnectionString"]);

            //Create the blob client object.
            CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

            //Get a reference to a container to use for the sample code, and create it if it does not exist.
            CloudBlobContainer container = blobClient.GetContainerReference(containerName);
            return await container.DeleteIfExistsAsync();
        }

        public async Task<IEnumerable<IListBlobItem>> GetContainerBlobListAsync(string sas, string prefix = "")
        {
            //Return a reference to the container using the SAS URI.
            CloudBlobContainer container = new CloudBlobContainer(new Uri(sas));

            //Create a list to store blob URIs returned by a listing operation on the container.
            var blobList = container.ListBlobs(prefix);

            return await Task.FromResult(blobList);
        }

        public async Task<bool> BlobItemExistsAsync(string sas, string fileName)
        {
            //Return a reference to the container using the SAS URI.
            CloudBlobContainer container = new CloudBlobContainer(new Uri(sas));

            //Get a reference to a blob within the container.
            CloudBlockBlob blob = container.GetBlockBlobReference(fileName);

            return await blob.ExistsAsync();
        }

        public async Task<string> GetBlobItemUriAsync(string sas, string fileName, bool withSas = false, string policyName = null, SharedAccessBlobPermissions permittion = SharedAccessBlobPermissions.Read | SharedAccessBlobPermissions.Write, DateTime? startTime = null, DateTime? expiryTime = null)
        {
            //Return a reference to the container using the SAS URI.
            CloudBlobContainer container = new CloudBlobContainer(new Uri(sas));

            //Get a reference to a blob within the container.
            CloudBlockBlob blob = container.GetBlockBlobReference(fileName);

            if (!await blob.ExistsAsync())
            {
                return null;
            }

            if (withSas)
            {
                //Set the expiry time and permissions for the blob.
                //In this case the start time is specified as a few minutes in the past, to mitigate clock skew.
                //The shared access signature will be valid immediately.
                SharedAccessBlobPolicy sasConstraints = new SharedAccessBlobPolicy();
                sasConstraints.SharedAccessStartTime = startTime.HasValue ? startTime.Value : DateTime.UtcNow.AddMinutes(-5);
                sasConstraints.SharedAccessExpiryTime = expiryTime.HasValue ? expiryTime.Value : DateTime.UtcNow.AddHours(24);
                sasConstraints.Permissions = permittion;

                //Generate the shared access signature on the blob, setting the constraints directly on the signature.
                string sasBlobToken = policyName == null ? blob.GetSharedAccessSignature(sasConstraints) : blob.GetSharedAccessSignature(sasConstraints, policyName);

                //Return the URI string for the container, including the SAS token.
                return await Task.FromResult(blob.Uri + sasBlobToken);
            }
            else
            {
                return await Task.FromResult(blob.Uri.ToString());
            }
        }

        public async Task ReadBlobAsync(string sas, string fileName, Stream target)
        {
            //Return a reference to the container using the SAS URI.
            CloudBlobContainer container = new CloudBlobContainer(new Uri(sas));

            //Create a list to store blob URIs returned by a listing operation on the container.
            var blob = await container.GetBlobReferenceFromServerAsync(fileName);

            await blob.DownloadToStreamAsync(target);
        }

        public async Task ReadBlobAsync(string sas, string fileName, Stream target, CancellationToken token)
        {
            //Return a reference to the container using the SAS URI.
            CloudBlobContainer container = new CloudBlobContainer(new Uri(sas));

            //Create a list to store blob URIs returned by a listing operation on the container.
#if DNXCORE50
            var blob = await container.GetBlobReferenceFromServerAsync(fileName,
                AccessCondition.GenerateEmptyCondition(),
                new BlobRequestOptions(),
                new OperationContext(),
                token);
#else
            var blob = await container.GetBlobReferenceFromServerAsync(fileName, token);
#endif

            await blob.DownloadToStreamAsync(target);
        }

        public async Task<string> UploadBlobAsync(string sas, string fileName, Stream source, bool overwritten = true, string policyName = null, SharedAccessBlobPermissions permittion = SharedAccessBlobPermissions.Read | SharedAccessBlobPermissions.Write, DateTime? startTime = null, DateTime? expiryTime = null)
        {
            //Return a reference to the container using the SAS URI.
            CloudBlobContainer container_fromSas = new CloudBlobContainer(new Uri(sas));

            //Parse the connection string and return a reference to the storage account.
            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(this.Configuration["StorageConnectionString"]);

            //Create the blob client object.
            CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

            //Get a reference to a container to use for the sample code, and create it if it does not exist.
            CloudBlobContainer container = blobClient.GetContainerReference(container_fromSas.Name);

            return await UploadAndGetBlobSasUriAsync(container, fileName, source, overwritten, policyName, permittion, startTime, expiryTime);
        }

    }

}